<?php
/**
*	@package UHU_Components
*	@subpackage Output
*/

uhu::load_components('Smarty','PDO doc cache');

/**
*	Interface to output class
*	@package Output
*/
interface uhu_output_funcs
{
/**
*	Returns the expected lifetime of the cached document
*	@return integer Cache lifetime in seconds
*/
	public static function cache_lifetime();
/**
*	Intended to allow the object to configure Smarty for it's own needs
*/
	public function config_Smarty();
/**
*	Allows an output class to handle it's own Exceptions
*	@param Exception $e The instance of an Exception class thrown by application code
*/
	public static function Exception_handler(Exception $e);
/**
*	Provides a means for output objects to call get_class($this)
*	@return string The name of the class instantiated
*/
	public function get_class_name();
/**
*	Attempts to fetch the document from the cache
*	@return string|bool The cached document, FALSE on failure
*/
	public function fetch();
/**
*	Handles the display of the cached document
*/
	public function display();
}

/**
*	Base Smarty_Conf class for use with {@link uhu_output::__construct()}
*	@package Smarty
*	@subpackage Output
*/
abstract class uhu_output_Smarty_Conf extends Smarty_Conf
{
	public function __toString()
	{
		return md5('templates:' . $this->template_dir() . "\n" . 'compile:' . $this->compile_dir() . "\n" . 'cache:' . $this->cache_dir());
	}
	public function cache_dir()
	{
		return null;
	}
}

/**
*	Root class for handling document output for UHU application code
*	@package Output
*/
abstract class uhu_output
{
/**
*	The Smarty template used for the output
*	@return string name of Smarty template
*/
	abstract protected function Smarty_filename();
/**
*	The filename to use with {@link PDO_doc_cache}
*	@return string filename
*/
	abstract protected function PDO_doc_cache_filename();
/**
*	The Cache ID, used by both {@link Smarty} and {@link PDO_doc_cache}
*	@return string cache ID
*/
	abstract protected function PDO_doc_cache_cache_id();
/**
*	The Compile ID, used by both {@link Smarty} and {@link PDO_doc_cache}
*	@return string compile ID
*/
	abstract protected function PDO_doc_cache_compile_id();

/**
*	 An instance of {@link Smarty}
*	@var Smarty
*/
	protected $Smarty;
/**
*	An instance of {@link PDO_doc_cache}
*	@var PDO_doc_cache
*/
	protected $PDO_doc_cache;

/**
*	Builds the Output object for the application code
*	@param Smarty_Conf $Smarty_Conf The configuration object used to configure {@link Smarty_Registry}
*	@param PDO_doc_cache $PDO_doc_cache The PDO_doc_cache instance used to store/retrieve the cached document
*	@uses uhu_output::get_class_name()
*	@uses uhu_output::Exception_handler()
*	@uses uhu_output::$PDO_doc_cache
*	@uses PDO_doc_cache::set_callback()
*	@uses PDO_doc_cache::Smarty_cache_handler()
*	@uses uhu_output::cache_lifetime()
*	@uses uhu_output::config_Smarty
*/
	public function __construct(Smarty_Conf $Smarty_Conf,PDO_doc_cache $PDO_doc_cache)
	{
		set_exception_handler($this->get_class_name() . '::Exception_handler');
		$this->PDO_doc_cache = $PDO_doc_cache;
		$this->Smarty = Smarty_Registry::get($Smarty_Conf);
		$PDO_doc_cache->set_callback(
			create_function('$document','return gzcompress($document,9);'),
			create_function('$document','return gzuncompress($document);')
		);
		$this->Smarty->cache_handler_func = array($PDO_doc_cache,'Smarty_cache_handler');
		$this->Smarty->caching = 1;
		$this->Smarty->cache_lifetime = call_user_func($this->get_class_name() . '::cache_lifetime');
		$this->Smarty->compile_check = true;
		$this->Smarty->cache_modified_check = true;
		if($this->Smarty->is_cached($this->Smarty_filename(),$this->PDO_doc_cache_cache_id(),$this->PDO_doc_cache_compile_id()) === false)
		{
			call_user_func(array($this,'config_Smarty'));
		}
	}

/**
*	@see uhu_output_funcs::get_class_name()
*	@return string
*/
	public function get_class_name()
	{
		static $c;
		if(isset($c) === false)
		{
			$c = get_class($this);
		}
		return $c;
	}
/**
*	@see uhu_output_funcs::fetch()
*	@return string
*/
	public function fetch()
	{
		return $this->Smarty->fetch(
			$this->Smarty_filename(),
			$this->PDO_doc_cache_cache_id(),
			$this->PDO_doc_cache_compile_id()
		);
	}
/**
*	@see uhu_output_funcs::display()
*	@return string
*/
	public function display()
	{
		$cache_info = $this->Smarty->_cache_info;
		$created = $_SERVER['REQUEST_TIME'];
		$expires = $created + $this->Smarty->cache_lifetime;
		if(empty($cache_info) === false)
		{
			$created = $cache_info['timestamp'];
			$expires = $cache_info['expires'];
			$If_Modified_Since = uhu_request_config::getInstance()->If_Modified_Since();
			if(isset($If_Modified_Since) && strtotime($If_Modified_Since) < $created)
			{
				headerBySAPI('HTTP/1.1 304 Not Modified');
				exit;
			}
		}
		$doc = $this->fetch();
		if(uhu_request_config::getInstance()->gzip())
		{
			$doc = gzencode($doc,uhu_request_config::GZIP_COMPRESSION_LEVEL);
			header('Content-Encoding:gzip');
		}
		header('Content-Length:' . strlen($doc));
		header('Expires:' . gmdate('r',$expires));
		header('Last-Modified:' . gmdate('r',$created));
		header('Cache-Control: max-age=' . ($expires - $created) . ', must-revalidate, public, proxy-revalidate');
		die($doc);
	}
}
?>